package com.mxpio.erp.sales.service.impl;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mxpio.erp.common.enums.NoticeStatusEnums;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.inventory.entity.InvFlow;
import com.mxpio.erp.common.inventory.entity.VInvStock;
import com.mxpio.erp.common.inventory.entity.WarehouseOrder;
import com.mxpio.erp.common.inventory.entity.WarehouseOrderLine;
import com.mxpio.erp.common.jpa.model.Notice;
import com.mxpio.erp.common.jpa.model.NoticeLine;
import com.mxpio.erp.common.jpa.model.OrderLineKey;
import com.mxpio.erp.common.sales.entity.*;
import com.mxpio.erp.common.sales.service.SalesOrderLineService;
import com.mxpio.erp.common.sales.vo.SOLineExecDetailVO;
import com.mxpio.erp.common.sales.vo.SOLinePlanDetailVO;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.workshop.entity.VWorkShopPassRateDetail;
import com.mxpioframework.common.util.BeanReflectionUtils;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.lin.Linq;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Operator;
import com.mxpioframework.jpa.query.Order;
import com.mxpioframework.system.service.impl.BaseServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SalesOrderLineServiceImpl extends BaseServiceImpl<SalesOrderLine> implements SalesOrderLineService {
    @Transactional(readOnly = false)
    @Override
    public List<SalesOrderLine> list(Criteria c){
        /*Map<String,SalesOrder> salesOrderMap = new HashMap<>();
        List<SalesOrderLine> salesOrderLines = this.list(SalesOrderLine.class, c);
        for(SalesOrderLine salesOrderLine:salesOrderLines){
            //gettingQuantity(salesOrderLine);
            SalesOrder salesOrder = salesOrderMap.get(salesOrderLine.getBizNo());
            if(salesOrder==null){
                salesOrder = JpaUtil.linq(SalesOrder.class).idEqual(salesOrderLine.getBizNo()).findOne();
                salesOrderMap.put(salesOrderLine.getBizNo(),salesOrder);
            }
            salesOrderLine.setSalesOrder(salesOrder);
        }*/
        return JpaUtil.linq(SalesOrderLine.class).collect("bizNo", SalesOrder.class , "bizNo").where(c).list();
    }

    @Transactional(readOnly = false)
    @Override
    public List<SalesOrderLine> auditList(Criteria c){
        List<SalesOrderLine> salesOrderLineList = list(c);
        if(CollectionUtils.isNotEmpty(salesOrderLineList)){
            salesOrderLineList.removeIf(line -> !line.getSalesOrder().getBpmnStatus().equals(OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode()));
        }
        return salesOrderLineList;
    }

    @Transactional(readOnly = false)
    @Override
    public Page<SalesOrderLine> listPage(Pageable page, Criteria c){
		return JpaUtil.linq(SalesOrderLine.class)
					.collect("bizNo", SalesOrder.class, "bizNo")
					.exists(SalesOrder.class)
						.equalProperty("bizNo", "bizNo")
                        .notEqual("closeStatus", OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())
                        .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                        .notEqual("orderStatus", OrderStatusEnums.OrderStatus.CLEARED.getStatusCode())
					.end()
					.where(c)
					.paging(page);
    }

    @Transactional
    @Override
    public List<SalesOrderLine> listPageAll(Criteria c){
        return JpaUtil.linq(SalesOrderLine.class)
                .collect("bizNo", SalesOrder.class, "bizNo")
                .exists(SalesOrder.class)
                    .equalProperty("bizNo", "bizNo")
                    .notEqual("closeStatus", OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())
                    .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                    .notEqual("orderStatus", OrderStatusEnums.OrderStatus.CLEARED.getStatusCode())
                .end()
                .where(c)
                .list();
    }

    @Transactional(readOnly = false)
    @Override
    public Page<SalesOrderLine> listPageUnFinished(Pageable page, Criteria c){
        return JpaUtil.linq(SalesOrderLine.class)
                .collect("bizNo", SalesOrder.class, "bizNo")
                .notEqual("closeStatus", OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())
                .exists(SalesOrder.class)
                    .equalProperty("bizNo", "bizNo")
                    .notEqual("closeStatus", OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())
                    .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                    .notEqual("orderStatus", OrderStatusEnums.OrderStatus.CLEARED.getStatusCode())
                    .notEqual("orderStatus", OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())
                .end()
                .where(c)
                .paging(page);
    }

    @Transactional(readOnly = false)
    @Override
    public SalesOrderLine getById(OrderLineKey id){
        SalesOrderLine salesOrderLine = this.getById(SalesOrderLine.class,id);
        if(salesOrderLine!=null){
            SalesOrder salesOrder = JpaUtil.linq(SalesOrder.class).idEqual(salesOrderLine.getBizNo()).findOne();
            salesOrderLine.setSalesOrder(salesOrder);
        }
        return salesOrderLine;
    }

    /*private void gettingQuantity(SalesOrderLine salesOrderLine){
        salesOrderLine.setDeliverQuantity(BigDecimal.ZERO);
        salesOrderLine.setActualMaxQuantity(salesOrderLine.getQuantity());
        salesOrderLine.setPlanQuantity(BigDecimal.ZERO);
        salesOrderLine.setPlanBackQuantity(BigDecimal.ZERO);
        salesOrderLine.setActualBackQuantity(BigDecimal.ZERO);
        //计划发货数
        List<ShippingNotice> noticeList = JpaUtil.linq(ShippingNotice.class).equal("bizOrderNo", salesOrderLine.getBizNo()).list();
        if(CollectionUtils.isEmpty(noticeList)){
            return;
        }
        Set<String> noticeNoSet = noticeList.stream().map(ShippingNotice::getNoticeNo).collect(Collectors.toSet());
        List<ShippingNoticeLine> noticeLineList = JpaUtil.linq(ShippingNoticeLine.class).in("noticeNo",noticeNoSet).equal("itemCode",salesOrderLine.getItemCode()).list();
        BigDecimal planQuantity = noticeLineList.stream().map(ShippingNoticeLine::getQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
        salesOrderLine.setPlanQuantity(planQuantity);
        salesOrderLine.setActualMaxQuantity(salesOrderLine.getActualMaxQuantity().subtract(planQuantity));
        //实际发货数
        BigDecimal deliverQuantity = getActual(salesOrderLine,OrderEnums.AccessType.OUT.getCode());
        if(deliverQuantity==null){
            return;
        }
        salesOrderLine.setDeliverQuantity(deliverQuantity);
        //计划发货数不包括实际发货数
        salesOrderLine.setPlanQuantity(planQuantity.subtract(deliverQuantity));


        //计划退货数
        List<RejectBill> rejectList = JpaUtil.linq(RejectBill.class).in("bizOrderNo",noticeNoSet).list();
        if(CollectionUtils.isEmpty(rejectList)){
            return;
        }
        Set<String> rejectNoticeNoSet = rejectList.stream().map(RejectBill::getNoticeNo).collect(Collectors.toSet());
        List<RejectBillLine> rejectLineList = JpaUtil.linq(RejectBillLine.class).in("noticeNo",rejectNoticeNoSet).equal("itemCode",salesOrderLine.getItemCode()).list();
        BigDecimal planBackQuantity = rejectLineList.stream().map(RejectBillLine::getQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
        salesOrderLine.setPlanBackQuantity(planBackQuantity);

        salesOrderLine.setActualMaxQuantity(salesOrderLine.getActualMaxQuantity().add(planBackQuantity));

        //实际退货数
        BigDecimal actualBackQuantity = getActual(salesOrderLine,OrderEnums.AccessType.IN.getCode());
        if(actualBackQuantity==null){
            return;
        }
        salesOrderLine.setActualBackQuantity(actualBackQuantity);
        //计划退货数不包括实际退货数
        salesOrderLine.setPlanBackQuantity(planBackQuantity.subtract(actualBackQuantity));


    }*/

    /*private BigDecimal getActual(SalesOrderLine salesOrderLine,String accessType){
        Set<String> processingStatus = new HashSet<>();
        processingStatus.add(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode());
        processingStatus.add(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());
        processingStatus.add(OrderStatusEnums.OrderStatus.CLEARED.getStatusCode());
        List<WarehouseOrder> warehouseOrderList = JpaUtil.linq(WarehouseOrder.class).equal("originBizNo",salesOrderLine.getBizNo()).in("orderStatus", processingStatus).equal("accessType",accessType).list();
        if(CollectionUtils.isEmpty(warehouseOrderList)){
            return null;
        }
        Set<String> warehouseOrderNoSet = warehouseOrderList.stream().map(WarehouseOrder::getBizNo).collect(Collectors.toSet());
        List<WarehouseOrderLine> warehouseOrderLineList = JpaUtil.linq(WarehouseOrderLine.class).in("bizNo",warehouseOrderNoSet).equal("itemCode",salesOrderLine.getItemCode()).list();
        return warehouseOrderLineList.stream().map(WarehouseOrderLine::getQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
    }*/

    @Override
    @Transactional
    public List<SOLineExecDetailVO> getExecDetails(String bizNo, String lineNo, String accessType) {
        OrderLineKey lineKey = new OrderLineKey();
        lineKey.setLineNo(lineNo);
        lineKey.setBizNo(bizNo);
        SalesOrderLine soline = this.getById(lineKey);
        if (soline==null){
            return Lists.newArrayList();
        }
        Criteria criteria = Criteria.create();
        criteria.addOrder(new Order("createTime",false));
        List<InvFlow> flows = JpaUtil.linq(InvFlow.class)
                //.equal("woBizType", NoticeStatusEnums.NoticeType.SO_OUT.getCode())
                .equal("originBizNo", bizNo)
                .equal("originBizLineNo",lineNo)
                .equal("accessType",accessType)
                .where(criteria)//排序
                .list();
        if (flows.isEmpty()){
            return new ArrayList<>();
        }
        List<SOLineExecDetailVO> returnList = Lists.newArrayList();
        Map<String,WarehouseOrderLine> tempWOLineMap = Maps.newHashMap();
        for (InvFlow flow:flows){
            SOLineExecDetailVO soLineExecDetailVO = new SOLineExecDetailVO();
            BeanReflectionUtils.copyProperties(soLineExecDetailVO,flow);
            String mapKey = flow.getWoBizNo()+flow.getWoBizLineNo();
            WarehouseOrderLine warehouseOrderLine = null;
            if (tempWOLineMap.containsKey(mapKey)){
                warehouseOrderLine = tempWOLineMap.get(mapKey);
            }else{
                List<WarehouseOrderLine> wolineList = JpaUtil.linq(WarehouseOrderLine.class).equal("bizNo", flow.getWoBizNo()).equal("lineNo", flow.getWoBizLineNo()).list();
                if(!wolineList.isEmpty()){
                    warehouseOrderLine = wolineList.get(0);
                }
            }
            if (warehouseOrderLine!=null){
                soLineExecDetailVO.setOriginNoticeNo(warehouseOrderLine.getOriginNoticeNo());
                soLineExecDetailVO.setOriginNoticeLineNo(warehouseOrderLine.getOriginNoticeLineNo());
            }
            returnList.add(soLineExecDetailVO);
        }
        return returnList;
    }

    @Override
    @Transactional
    public List<SOLinePlanDetailVO> getPlanDetails(String bizNo, String lineNo, String accessType) {
        OrderLineKey lineKey = new OrderLineKey();
        lineKey.setLineNo(lineNo);
        lineKey.setBizNo(bizNo);
        SalesOrderLine soline = this.getById(lineKey);
        if (soline==null){
            return Lists.newArrayList();
        }
        List<SOLinePlanDetailVO> returnList = Lists.newArrayList();
        List<WarehouseOrder> wos = null;
        if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {//计划出
            //未确认的通知单
            List<Notice> noticeList = JpaUtil.linq(ShippingNotice.class).collect("noticeNo", ShippingNoticeLine.class, "noticeNo")
                    .equal("noticeStatus", NoticeStatusEnums.NoticeStatus.CREATE.getStatusCode())
                    .equal("bizOrderNo", bizNo)
                    .list();
            if (!noticeList.isEmpty()){
                for(Notice notice : noticeList){
                    for(NoticeLine line : notice.getNoticeLines()){
                        if (line.getOriginBizLineNo().equals(lineNo)){
                            SOLinePlanDetailVO soLinePlanDetailVO = new SOLinePlanDetailVO();
                            BeanReflectionUtils.copyProperties(soLinePlanDetailVO,line);
                            returnList.add(soLinePlanDetailVO);
                        }
                    }
                }
            }
            //未确认的仓单
            wos = JpaUtil.linq(WarehouseOrder.class)
                    .equal("closeStatus", OrderStatusEnums.CloseStatus.OPEN.getStatusCode())
                    .notEqual("orderStatus", OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())
                    .equal("woType", NoticeStatusEnums.NoticeType.SO_OUT.getCode())
                    .equal("originBizNo", bizNo).list();
        }else{//计划退
            //未确认的通知单
            List<Notice> noticeList = JpaUtil.linq(RejectBill.class).collect("noticeNo", RejectBillLine.class, "noticeNo")
                    .equal("noticeStatus", NoticeStatusEnums.NoticeStatus.CREATE.getStatusCode())
                    .equal("bizOrderNo", bizNo)
                    .list();
            if (!noticeList.isEmpty()){
                for(Notice notice : noticeList){
                    for(NoticeLine line : notice.getNoticeLines()){
                        if (line.getOriginBizLineNo().equals(lineNo)){
                            SOLinePlanDetailVO soLinePlanDetailVO = new SOLinePlanDetailVO();
                            BeanReflectionUtils.copyProperties(soLinePlanDetailVO,line);
                            returnList.add(soLinePlanDetailVO);
                        }
                    }
                }
            }
            //未确认的仓单
            wos = JpaUtil.linq(WarehouseOrder.class)
                    .equal("closeStatus", OrderStatusEnums.CloseStatus.OPEN.getStatusCode())
                    .notEqual("orderStatus", OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())
                    .equal("woType", NoticeStatusEnums.NoticeType.SO_IN.getCode())
                    .equal("originBizNo", bizNo).list();
        }
        if (!wos.isEmpty()){
            //合计已执行的数量
            Set<String> woBinNoSet = wos.stream().map(WarehouseOrder::getBizNo).collect(Collectors.toSet());
            List<InvFlow> invFlows = JpaUtil.linq(InvFlow.class).in("woBizNo", woBinNoSet).list();
            Map<String, BigDecimal> flowMap = new HashMap<>();
            invFlows.forEach(flow -> {
                BigDecimal sumQuantity = flowMap.get(flow.getWoBizNo()+flow.getWoBizLineNo());
                if(sumQuantity == null){
                    sumQuantity = BigDecimal.ZERO;
                }
                sumQuantity = sumQuantity.add(flow.getQuantity());
                flowMap.put(flow.getWoBizNo()+flow.getWoBizLineNo(), sumQuantity);
            });
            for (WarehouseOrder wo:wos){
                List<WarehouseOrderLine> woLines = JpaUtil.linq(WarehouseOrderLine.class)
                        .equal("bizNo", wo.getBizNo())
                        .equal("originBizLineNo",lineNo)
                        .equal("closeStatus", OrderStatusEnums.CloseStatus.OPEN.getStatusCode()).list();

                if (!woLines.isEmpty()){
                    for (WarehouseOrderLine line:woLines){
                        BigDecimal execQuantity = BigDecimal.ZERO;
                        if (flowMap.containsKey(line.getBizNo() + line.getLineNo())){
                            execQuantity = flowMap.get(line.getBizNo() + line.getLineNo());
                        }
                        if (line.getQuantity().compareTo(execQuantity)!=0){
                            if (OrderEnums.AccessType.OUT.getCode().equals(accessType)){//
                                ShippingNoticeLine noticeLine = JpaUtil.linq(ShippingNoticeLine.class)
                                        .equal("noticeNo", line.getOriginNoticeNo())
                                        .equal("lineNo", line.getOriginNoticeLineNo()).findOne();

                                SOLinePlanDetailVO soLinePlanDetailVO = new SOLinePlanDetailVO();
                                BeanReflectionUtils.copyProperties(soLinePlanDetailVO,noticeLine);
                                soLinePlanDetailVO.setQuantity(line.getQuantity().subtract(execQuantity));//计划执行数量
                                soLinePlanDetailVO.setWoBizNo(line.getBizNo());
                                soLinePlanDetailVO.setWoBizLineNo(line.getLineNo());
                                soLinePlanDetailVO.setWhCode(wo.getWhCode());
                                returnList.add(soLinePlanDetailVO);
                            }else{
                                RejectBillLine noticeLine = JpaUtil.linq(RejectBillLine.class)
                                        .equal("noticeNo", line.getOriginNoticeNo())
                                        .equal("lineNo", line.getOriginNoticeLineNo()).findOne();

                                SOLinePlanDetailVO soLinePlanDetailVO = new SOLinePlanDetailVO();
                                BeanReflectionUtils.copyProperties(soLinePlanDetailVO,noticeLine);
                                soLinePlanDetailVO.setQuantity(line.getQuantity().subtract(execQuantity));//计划执行数量
                                soLinePlanDetailVO.setWoBizNo(line.getBizNo());
                                soLinePlanDetailVO.setWoBizLineNo(line.getLineNo());
                                soLinePlanDetailVO.setWhCode(wo.getWhCode());
                                returnList.add(soLinePlanDetailVO);
                            }
                        }
                    }
                }
            }
        }
        return returnList;
    }

    @Override
    @Transactional
    public void getDefaultWhCode(List<SalesOrderLine> list) {
        if (!list.isEmpty()){
            Set<String> itemCodeSet = list.stream().map(SalesOrderLine::getItemCode).collect(Collectors.toSet());
            List<Item> itemList = JpaUtil.linq(Item.class).in("itemCode", itemCodeSet).list();
            Map<String, Item> itemMap = JpaUtil.index(itemList, "itemCode");


            Linq linq = JpaUtil.linq(VInvStock.class);
            List<VInvStock> stockList = linq.aliasToBean(VInvStock.class).select("itemCode",
                            linq.criteriaBuilder().sum(linq.root().get("allQuantity")).alias("allQuantity"))
                    .groupBy( "itemCode")
                    .list();
            Map<String, VInvStock> itemStockMap = JpaUtil.index(stockList, "itemCode");
            for (SalesOrderLine line:list){
                if (itemMap.containsKey(line.getItemCode())){
                    line.setWhCode(itemMap.get(line.getItemCode()).getDefaultWhCode());
                }
                if (itemStockMap!=null && itemStockMap.containsKey(line.getItemCode())){
                    line.setLiveStockQuantity(itemStockMap.get(line.getItemCode()).getAllQuantity());
                }
            }
        }
    }
}
